﻿//---------------------------------------------------------------------------
// <copyright file="RibbonContextualTabGroup.cs" company="Microsoft Corporation">
//     Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon
{
    #region Using declarations

    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Markup;
    using System.Windows.Media;

    #endregion

    /// <summary>
    ///   Implements a contextual tab group.  This is a RibbonTab group which
    ///   is only active under certain conditions.
    /// </summary>
    [ContentProperty("Tabs")]
    public class RibbonContextualTabGroup : Control
    {
        #region Fields

        /// <summary>
        ///   Backing store for the Tabs property.
        /// </summary>
        private ObservableCollection<RibbonTab> _tabs;

        /// <summary>
        ///   Indicates whether the mouse is pressed and currently over the RibbonContextualTabGroup.
        /// </summary>
        private bool _pressed = false;

        #endregion

        #region Constructors

        /// <summary>
        ///   Initializes static members of the RibbonContextualTabGroup class.  Also
        ///   overrides the default style.
        /// </summary>
        static RibbonContextualTabGroup()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(typeof(RibbonContextualTabGroup)));
        }

        #endregion

        #region Public Events

        /// <summary>
        ///   Adds or removes handlers for the Activated event.
        /// </summary>
        public event RoutedEventHandler Activated
        {
            add { AddHandler(ActivatedEvent, value, false); }
            remove { RemoveHandler(ActivatedEvent, value); }
        }

        /// <summary>
        ///   An event raised when the RibbonContextualTabGroup is activated.
        /// </summary>
        public static readonly RoutedEvent ActivatedEvent =
                    EventManager.RegisterRoutedEvent(
                            "Activated",
                            RoutingStrategy.Direct,
                            typeof(RoutedEventHandler),
                            typeof(RibbonContextualTabGroup));

        /// <summary>
        ///   Adds or removes handlers for the Deactivated event.
        /// </summary>
        public event RoutedEventHandler Deactivated
        {
            add { AddHandler(DeactivatedEvent, value, false); }
            remove { RemoveHandler(DeactivatedEvent, value); }
        }

        /// <summary>
        ///   An event raised when the RibbonContextualTabGroup is deactivated.
        /// </summary>
        public static readonly RoutedEvent DeactivatedEvent =
                    EventManager.RegisterRoutedEvent(
                            "Deactivated",
                            RoutingStrategy.Direct,
                            typeof(RoutedEventHandler),
                            typeof(RibbonContextualTabGroup));

        #endregion

        #region Public Properties

        /// <summary>
        ///   Gets the ContextualTabGroup's tabs collection property.
        /// </summary>
        public Collection<RibbonTab> Tabs
        {
            get
            {
                if (_tabs == null)
                {
                    _tabs = new ObservableCollection<RibbonTab>();
                    _tabs.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnTabCollectionChanged);
                }

                return _tabs;
            }
        }

        /// <summary>
        ///   Gets or sets the label of the contextual tab group.
        /// </summary>
        public string Label
        {
            get { return (string)GetValue(LabelProperty); }
            set { SetValue(LabelProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for LabelProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty LabelProperty =
                    DependencyProperty.Register(
                            "Label",
                            typeof(string),
                            typeof(RibbonContextualTabGroup),
                            new FrameworkPropertyMetadata(String.Empty));

        /// <summary>
        ///   Gets or sets the color of the contextual tab groups 'glow' effect on the title bar.
        /// </summary>
        public Color Color
        {
            get { return (Color)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for ColorProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty ColorProperty =
                    DependencyProperty.Register(
                            "Color",
                            typeof(Color),
                            typeof(RibbonContextualTabGroup),
                            new FrameworkPropertyMetadata(Color.FromArgb(0x20, 0x20, 0x00, 0xFF), new PropertyChangedCallback(OnColorPropertyChanged)));

        /// <summary>
        ///   Gets or sets a value indicating whether the contextual tab group is active.
        /// </summary>
        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for IsActiveProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IsActiveProperty =
                    DependencyProperty.Register(
                            "IsActive",
                            typeof(bool),
                            typeof(RibbonContextualTabGroup),
                            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsActiveChanged)));

        #endregion

        #region Internal Properties

        /// <summary>
        ///   Gets or sets a value indicating whether the contextual tab group's label should be shown.
        /// </summary>
        internal bool ShowLabelToolTip
        {
            get
            {
                return this.Label != null && VisualChild != null && this.Label.Equals(VisualChild.ToolTip);
            }

            set
            {
                if (value)
                {
                    if (this.ToolTip == null && VisualChild != null)
                    {
                        VisualChild.ToolTip = this.Label;
                    }
                }
                else
                {
                    if (this.ShowLabelToolTip && VisualChild != null)
                    {
                        VisualChild.ToolTip = null;
                    }
                }
            }
        }
        
        private FrameworkElement VisualChild
        {
            get
            {
                return VisualChildrenCount == 0 ? null : (GetVisualChild(0) as FrameworkElement);
            }
        }

        /// <summary>
        ///   Gets or sets the desired width of the contextual tab group.
        /// </summary>
        internal double DesiredWidth
        {
            get;
            set;
        }

        /// <summary>
        ///  Gets or sets the arrange width of the contextual tab group.
        /// </summary>
        internal double ArrangeWidth
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the arrange x-coordinate of the contextual tab group.
        /// </summary>
        internal double ArrangeX
        {
            get;
            set;
        }

        #endregion

        #region Protected Properties

        /// <summary>
        ///   Gets an enumerator for the logical children of the contextual tab group.  This
        ///   is what hooks up the logical tree.
        /// </summary>
        protected override IEnumerator LogicalChildren
        {
            get { return this.Tabs.GetEnumerator(); }
        }

        #endregion

        #region Private Properties

        /// <summary>
        ///   Gets the contextual tab group's parent Ribbon control.
        /// </summary>
        private Ribbon Ribbon
        {
            get { return this.Parent as Ribbon; }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        ///   Callback for mouse down.  Captures/Releases the mouse depending on whether
        ///   the tab group was clicked.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            if (e.ClickCount < 2)
            {
                this.CaptureMouse();
                if (this.IsMouseCaptured)
                {
                    // Though we have already checked this state, our call to CaptureMouse
                    // could also end up changing the state, so we check it again.
                    if (e.ButtonState == MouseButtonState.Pressed)
                    {
                        _pressed = true;
                    }
                    else
                    {
                        // Release capture since we decided not to press the button.
                        this.ReleaseMouseCapture();
                    }
                }

                e.Handled = true; // Handle the single click only, double click should be handled by Ribbon
            }

            base.OnMouseDown(e);
        }

        /// <summary>
        ///   Callback for mouse up, releases mouse capture and sends click notifications.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            if (this.IsMouseCaptured)
            {
                this.ReleaseMouseCapture();
            }

            if (_pressed)
            {
                _pressed = false;
                if (this.Ribbon != null)
                {
                    this.Ribbon.NotifyMouseClickedOnContextualTabGroup(this);
                }
            }

            base.OnMouseUp(e);
        }

        /// <summary>
        ///   Callback for mouse move, this updates the effective pressed state of left mouse button.
        /// </summary>
        /// <param name="e">The event data.</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (this.IsMouseCaptured && (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed))
            {
                this.UpdateIsPressed();
                e.Handled = true;
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        ///   Callback for property change notifications for the color property.  Here we recreate the
        ///   brushes used to draw the contextual tab group's glow effect.
        /// </summary>
        /// <param name="o">The RibbonContextualTabGroup whose Color property changed.</param>
        /// <param name="e">The event data.</param>
        private static void OnColorPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            RibbonContextualTabGroup group = (RibbonContextualTabGroup)o;

            Color color = group.Color;
            if (color != null)
            {
                LinearGradientBrush background = new LinearGradientBrush();
                background.StartPoint = new Point(0, 1);
                background.EndPoint = new Point(0, 0);
                background.GradientStops.Add(new GradientStop(Color.FromArgb(0x77, color.R, color.G, color.B), 0));
                background.GradientStops.Add(new GradientStop(Color.FromArgb(0x09, 0xFF, 0xFF, 0xFF), 1));
                background.Freeze();
                group.Background = background;

                LinearGradientBrush borderBrush = new LinearGradientBrush();
                borderBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0x44, color.R, color.G, color.B), 0));
                borderBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, color.R, color.G, color.B), 0.35));
                borderBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF, color.R, color.G, color.B), 0.65));
                borderBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0x44, color.R, color.G, color.B), 1));
                borderBrush.Freeze();
                group.BorderBrush = borderBrush;
            }
        }

        /// <summary>
        ///   Callback for IsActive property changed.  Activates or deactivates the RibbonContextualTabGroup.
        /// </summary>
        /// <param name="sender">The RibbonContextualTabGroup whose IsActive property changed.</param>
        /// <param name="e">The event data.</param>
        private static void OnIsActiveChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            RibbonContextualTabGroup contextualTabGroup = (RibbonContextualTabGroup)sender;

            if (contextualTabGroup.IsActive)
            {
                contextualTabGroup.Activate();
            }
            else
            {
                contextualTabGroup.Deactivate();
            }
        }

        /// <summary>
        ///   Called whenever the collection of tabs for the contextual tab group changes.  This
        ///   is where we maintain the logical tree.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">The information for the event.</param>
        private void OnTabCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // This notification is used to establish the logical tree linksbetween RibbonContextualTabGroup and RibbonTab
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (RibbonTab tab in e.NewItems)
                    {
                        this.AddLogicalChild(tab);
                    }

                    if (this.IsActive && this.Ribbon != null)
                    {
                        this.Ribbon.ActivateContextualTabGroup(this);
                    }

                    break;
            }
        }

        /// <summary>
        ///   Activates the RibbonContextualTabGroup and raises a notification.
        /// </summary>
        private void Activate()
        {
            if (this.Ribbon != null)
            {
                this.Ribbon.ActivateContextualTabGroup(this);
            }

            this.RaiseEvent(new RoutedEventArgs(RibbonContextualTabGroup.ActivatedEvent));
        }

        /// <summary>
        ///   Deactivates the RibbonContextualTabGroup and raises a notification.
        /// </summary>
        private void Deactivate()
        {
            if (this.Ribbon != null)
            {
                this.Ribbon.DeactivateTabGroup(this);
            }

            this.RaiseEvent(new RoutedEventArgs(RibbonContextualTabGroup.DeactivatedEvent));
        }

        /// <summary>
        ///   Updates whether the mouse is pressed over the RibbonContextualTabGroup.
        /// </summary>
        private void UpdateIsPressed()
        {
            Point pos = Mouse.PrimaryDevice.GetPosition(this);
            if ((pos.X >= 0) && (pos.X <= this.ActualWidth) && (pos.Y >= 0) && (pos.Y <= this.ActualHeight))
            {
                _pressed = true;
            }
            else
            {
                _pressed = false;
            }
        }

        #endregion
    }
}